<?php

namespace app\models\tableModel\base;

use app\models\tableModel\BlockModel;
use app\service\tool\UtilsService;
use Yii;
use yii\db\Expression;
use yii\db\ExpressionInterface;

/**
 * 基类
 */
class Base extends \yii\db\ActiveRecord
{

    /**
     * 基础[SQL]
     * @var \yii\db\ActiveQuery
     */
    protected $baseSql;
    /**
     * 别名
     * @var string
     */
    protected $ails = '';
    /**
     * 条件
     * @var array
     */
    protected $where = [];
    /**
     * 排序
     * @var array
     */
    protected $orderBy = [];

    /**
     * 静态错误暂存
     * @var
     */
    public static $error_ = [];

    /**
     * 加载整体[Model]
     * @param bool $id 编号
     * @param string $scenario 场景
     * @param string $isLock 是否锁行
     * @return $this
     *  ` PS:[$id]空为何不返回[$model::find()]: 因为可能准确想返回条目是否存在查询结果可能null,返回在find报错。
     */
    final public static function loadModel($id = true, $scenario = 'default', $isLock = false)
    {

        // 实力化类
        $model = new static();

        ### 验证 + 查询
        // 编号 不存在直接返回空
        if (!$id) return null;
        // 条件非强制返回 - 需要格式化语句兼容锁行
        if ($id !== true) {
            // 格式化单条查询
            if (!is_array($id) && !strstr($id, '=')) {
                $primaryKey = static::primaryKey();
                $id = [$primaryKey[0] => is_array($id) ? array_values($id) : $id];
            }
            $sql = self::find()->where($id)->createCommand()->getRawSql();
            // 锁行
            $sql .= ' FOR UPDATE';
            // 最终条件查询
            $model = self::findBySql($sql)->one();
        }
        // 条目不存在
        if (!$model) return $model;

        ### 场景
        // 赋值合法 场景
        $sceList = array_keys($model->scenarios());
        if (in_array($scenario, $sceList)) $model->setScenario($scenario);

        return $model;
    }

    /**
     * 初始化并返回当前基础[SQL]
     * @return \yii\db\ActiveQuery
     */
    final public function getBaseSql()
    {

        ### 数据存在直接返回
        if ($this->baseSql) return $this->baseSql;

        ### 不存在初始化
        $this->baseSql = $this::find()->alias($this->ails)->where($this->where);

        ### 初始化排序
        // 别名添加
        $ails = empty($this->ails) ? '' : $this->ails . '.';
        // 是否已经有自定义排序
        if (property_exists($this, 'orderBy') && !empty($this->orderBy)) {
            $this->baseSql->orderBy($this->orderBy);
        } else { // 无自定义排序
            $this->baseSql->orderBy($ails . 'id desc');
        }

        return $this->baseSql;
    }

    /**
     * 指定别名
     * @param $ails
     * @return $this
     */
    final public function ails($ails = false)
    {
        $this->ails = $ails;
        return $this;
    }

    /**
     * 加载排序
     * @param string|array $sort 排序规则
     *  ` 字符串格式为：field => sortType | id => desc
     * @param bool $noCheck 无需验证
     * @return $this
     */
    final public function loadSort($sort, $noCheck = false)
    {

        // 条件不存在
        if (empty($sort)) return $this;

        // 无需验证
        if ($noCheck || $sort instanceof ExpressionInterface) {
            $this->orderBy = $sort;
            return $this;
        }

        ### 格式化
        // 将[, ]转为[,]
        if (is_string($sort)) {
            // 字符串替换
            $sort = str_replace(', ', ',', $sort);
            // 字符串分割
            $sort = explode(',', $sort);
        }

        ### 最终执行
        // 别名
        $ails = empty($this->ails) ? '' : $this->ails . '.';
        // 循环  条件是否有效
        $stagingSort = [];
        // 合法排序列表
        $typeList = [SORT_DESC, SORT_ASC, 'DESC', 'ASC'];
        // 允许列表 - 无需验证
        $toExpList = ['RAND()'];
        foreach ($sort as $k => $v) {

            // 字段别名
            $ailKey = $ails . $k;

            ### 做一定的过滤
            // 数组 - 过滤
            if (is_array($v)) continue;
            // 类型是[表达式]
            if ($v instanceof ExpressionInterface) {
                $stagingSort[$ailKey] = $v;
                continue;
            }
            // 无需验证列表
            if (in_array(strtoupper($v), $toExpList)) {
                $stagingSort[$ailKey] = new Expression(strtoupper($v));
                continue;
            }

            ### 字段验证
            // 数组模式：字段->排序类型
            if ($this->hasAttribute($k) && in_array($v, $typeList)) {
                $stagingSort[$ailKey] = $v;
                continue;
            }
            // 字符模式：'id DESC'
            $expResult = preg_match('/^(.*?)\s+(asc|desc)$/i', $v, $matches);
            if ($expResult && $this->hasAttribute($matches[1])) {
                $stagingSort[$ails . $matches[1]] = strcasecmp($matches[2], 'desc') ? SORT_ASC : SORT_DESC;
            }
        }

        // 条件最终赋值
        $this->orderBy = $stagingSort;

        return $this;
    }

    /**
     * 加载条件
     * @param $where
     * @return $this
     */
    final public function loadWhere($where)
    {

        // 条件不存在
        if (empty($where) || !is_array($where)) return $this;

        // 如果是一维数组则进行格式化为关联数组
        $canRetList = ['and', 'or', 'AND', 'OR'];
        if (empty($where[0]) || !in_array($where[0], $canRetList)) {
            foreach ($where as $k => $v) {
                $where[] = ['=', $k, $v];
                unset($where[$k]);
            }
            $where = array_merge(['AND'], $where);
        }

        // 别名添加
        $ails = empty($this->ails) ? '' : $this->ails . '.';
        // 循环  条件是否有效
        foreach ($where as $k => &$v) {

            // 非法过滤
            if (in_array($v, $canRetList)) continue;

            // 字段不存在过滤
            if (!$this->hasAttribute($v[1]) || !(isset($v[2]) && UtilsService::removeSpace($v[2]))) {
                unset($where[$k]);
                continue;
            }

            ### 条件渲染
            // 数组
            if (is_array($v[2]) && count($v[2]) > 0) $v[0] = 'IN';
            // 其他自定义 - 由每个单独[Model]重写方法[whereCustomFilter]实现
            if (method_exists($this, 'whereCustomFilter')) {
                $v = $this->whereCustomFilter($v);
            }

            ### 格式化
            // 最终添加别名键
            $v[1] = $ails . $v[1];
        }

        // 条件最终赋值
        $this->where = $where;

        return $this;
    }
}
